class_name Player
extends CharacterBody2D

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var coyote_jump_timer: Timer = $Timers/CoyoteJumpTimer
@onready var hearts_container: HBoxContainer = $HeartsContainer
@onready var invincible_timer: Timer = $Timers/InvincibleTimer
@onready var hurtbox: Area2D = $Hurtbox
@onready var dash_cooldown: Timer = $Timers/DashCooldown
@onready var hurt_sfx: AudioStreamPlayer = $Audios/HurtSFX
@onready var jump_sfx: AudioStreamPlayer = $Audios/JumpSFX
@onready var dash_sfx: AudioStreamPlayer = $Audios/DashSFX
@onready var death_sfx: AudioStreamPlayer = $Audios/DeathSFX
@onready var death_timer: Timer = $Timers/DeathTimer
@onready var interaction_icon: AnimatedSprite2D = $InteractionIcon
@onready var interaction_detector: Area2D = $InteractionDetector
@onready var death_counter_label: Label = $DeathCounterLabel

@onready var starting_position = global_position

@export var movement_data : PlayerMovementData

var air_jump = false
var facing_dir = 1
var isDashing = false
var just_wall_jumped = false
var isHurting = false
var isDying = false
var wasDying = false
var hadDying = false
var maxHeart: int = 3
var currentHeart: int = maxHeart
var interacting_with: Array[Interactable]
var respawn_position: Vector2
var death_count: int = 0

func _ready() -> void:
	add_to_group("player")
	respawn_position = starting_position
	update_death_counter()
	
	interaction_detector.area_entered.connect(_on_interaction_area_entered)
	interaction_detector.area_exited.connect(_on_interaction_area_exited)

func _physics_process(delta: float) -> void:
	interaction_icon.visible = not interacting_with.is_empty()
	if Input.is_action_just_pressed("interact") and interacting_with:
		interacting_with.back().interact()
	
	var xInput := Input.get_axis("move_left", "move_right")
	get_facing_dir(xInput)
	apply_gravity(delta)
	handle_wall_jump(delta)
	handle_jump()
	handle_dash()
	handle_acceleration(xInput, delta)
	apply_resistance(xInput, delta)
	apply_animations(xInput)
	invincible_time()
	player_die()
	player_rebirth()
	var was_on_floor = is_on_floor()
	move_and_slide()
	coyoteJump_timer(was_on_floor)
	just_wall_jumped = false

func set_respawn_position(position: Vector2) -> void:
	respawn_position = position

func _on_interaction_area_entered(area: Area2D) -> void:
	if area is Interactable:
		register_interactable(area)

func _on_interaction_area_exited(area: Area2D) -> void:
	if area is Interactable:
		unregister_interactable(area)

func register_interactable(v: Interactable) -> void:
	if v in interacting_with or isDying or wasDying or not is_instance_valid(v):
		return
	interacting_with.append(v)

func unregister_interactable(v: Interactable) -> void:
	if v in interacting_with:
		interacting_with.erase(v)

func get_facing_dir(xInput):
	if xInput > 0:
		facing_dir = 1
	elif xInput < 0:
		facing_dir = -1

func apply_gravity(delta):
	if not is_on_floor():
		velocity += get_gravity() * movement_data.gravity_scale * delta

func handle_jump():
	if is_on_floor() or is_on_wall(): air_jump = true
	#郊狼时间
	if is_on_floor() or coyote_jump_timer.time_left > 0.0:
		if Input.is_action_just_pressed("jump"):
			velocity.y = movement_data.jump_velocity
			jump_sfx.play()
	elif not is_on_floor():
		#实现长短跳，松开空格越快，跳跃高度越低
		if Input.is_action_just_released("jump") and velocity.y < movement_data.jump_velocity / 3 and not isDashing:
			velocity.y = movement_data.jump_velocity / 3
		#二段跳
		if Input.is_action_just_pressed("jump") and air_jump and not just_wall_jumped:
			velocity.y = movement_data.jump_velocity * 0.75
			air_jump = false
			jump_sfx.play()

func coyoteJump_timer(was_on_floor):
	var just_left_ledge = was_on_floor and not is_on_floor() and velocity.y >= 0
	if just_left_ledge:
		coyote_jump_timer.start()

func handle_acceleration(xInput, delta):
	#处理地面加速度
	if xInput != 0 and is_on_floor():
		velocity.x = move_toward(velocity.x, movement_data.speed * xInput, movement_data.ground_acceleration * delta)
	#处理空中加速度
	if xInput != 0 and not is_on_floor():
		velocity.x = move_toward(velocity.x, movement_data.speed * xInput, movement_data.air_acceleration * delta)

func apply_resistance(xInput, delta):
	#摩擦力
	if xInput == 0 and is_on_floor():
		velocity.x = move_toward(velocity.x, 0, movement_data.friction * delta)
	#空气阻力
	if xInput == 0 and not is_on_floor():
		velocity.x = move_toward(velocity.x, 0, movement_data.air_resistance * delta)

func apply_animations(xInput):
	if is_on_floor():
		if xInput == 0 and !isDashing and !isHurting:
			animated_sprite.play("Idle")
		elif isDashing:
			animated_sprite.play("Dash")
		elif isHurting:
			animated_sprite.play("Hurt")
		else:
			animated_sprite.play("Run")
	else:
		if isDashing:
			animated_sprite.play("Dash")
		elif isHurting:
			animated_sprite.play("Hurt")
		else:
			animated_sprite.play(("Jump"))
	
	#翻转动画
	if xInput > 0:
		animated_sprite.flip_h = false
	elif xInput < 0:
		animated_sprite.flip_h = true

func handle_dash():
	if Input.is_action_just_pressed("dash") and not isDashing:
		velocity.x = movement_data.dash_speed * facing_dir
		isDashing = true
		dash_cooldown.start()
		dash_sfx.play()
	if dash_cooldown.time_left > 0.6 and isDashing:
		velocity.x = movement_data.dash_speed * facing_dir
		velocity.y = 0
	if dash_cooldown.time_left == 0.6 and isDashing:
		velocity.x = 0
		velocity.y = 0
	if dash_cooldown.time_left == 0 or is_on_wall_only():
		isDashing = false

func handle_wall_jump(delta):
	if not is_on_wall_only(): return
	var wall_normal = get_wall_normal()
	if Input.is_action_just_pressed("jump"):
		velocity.x = wall_normal.x * movement_data.speed * 0.7
		velocity.y = movement_data.jump_velocity * 0.9
		just_wall_jumped = true
		jump_sfx.play()
	#滑墙
	else:
		velocity.y = move_toward(velocity.y, 60, movement_data.wall_resistance * delta)

func player_die():
	if isDying:
		death_timer.start()
		death_count += 1
		update_death_counter()
		SceneManage.set_global_death_count(death_count)
		isDying = false
		wasDying = true
	elif wasDying and death_timer.time_left > 0:
		death_sfx.play()
		Engine.time_scale = 0.1
		hadDying = true
		wasDying = false

func restore_health() -> void:
	currentHeart = maxHeart
	hearts_container.updateHearts(currentHeart)

func player_rebirth():
	if death_timer.time_left ==  0.0 and hadDying:
		global_position = respawn_position
		restore_health()
		Engine.time_scale = 1.0
		hadDying = false
		isHurting = false
		interacting_with.clear()

func invincible_time():
	if invincible_timer.time_left > 0.0:
		hurtbox.set_collision_mask_value(5, false)
	else:
		hurtbox.set_collision_mask_value(5, true)
		isHurting = false

func _on_hurtbox_area_entered(hurtbox: Area2D) -> void:
	isHurting = true
	currentHeart -= 1
	if !isDying:
		hurt_sfx.play()
		invincible_timer.start()
		#受伤后弹离陷阱区域
		velocity.y = movement_data.jump_velocity * 0.7
	
	if currentHeart > 0:
		hearts_container.updateHearts(currentHeart)
	#死亡
	else:
		hearts_container.updateHearts(currentHeart)
		isDying = true

func update_death_counter() -> void:
	if death_counter_label:
		death_counter_label.text = "Death: " + str(death_count)

func get_death_count() -> int:
	return death_count
